package com.strive.oauth2.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.strive.common.constant.CommonConstant;
import com.strive.common.context.TenantContextHolder;
import com.strive.common.model.dto.SysMenu;
import com.strive.oauth2.properties.SecurityProperties;
import com.strive.oauth2.util.AuthUtils;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.util.AntPathMatcher;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @description:
 * @author: bingcun.chen
 * @Date: 2022/3/3 10:45
 * @Version 1.0
 */
@Slf4j
public abstract class DefaultPermissionServiceImpl {
    @Autowired
    private SecurityProperties securityProperties;

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     * 查询当前用户拥有的资源权限
     * @param roleCodes 角色列表 多个以Code以','隔开
     * @return
     */
    public abstract List<SysMenu> findMenuByRoleCodes(String roleCodes);

    /**
     * 校验是否有权限
     * @param authentication SpringSecurity权限类
     * @param requestMethod 方法方式 POST,GET,PUT,DELETE
     * @param requestURI 返回的URI路径
     * @return
     */
    public boolean hasPermission(Authentication authentication,String requestMethod,String requestURI){
        //前端跨域OPTIONS请求预检放行 也可以通过前端配置代理实现
        if(HttpMethod.OPTIONS.name().equalsIgnoreCase(requestMethod)){
            return true;
        }

        //todo 需要增加断点看下authentication都有哪几种类型
        if(!(authentication instanceof AnonymousAuthenticationToken)){
            //如果没配置Url权限直接过 只针对开启校验的
            if(!securityProperties.getAuth().getUrlPermission().getEnable()){
                return true;
            }

            //如果是超级管理员admin不需要认证
            String userName = AuthUtils.getUsername(authentication);
            if(CommonConstant.ADMIN_USER_NAME.equals(userName)){
                return true;
            }

            OAuth2Authentication auth2Authentication = (OAuth2Authentication)authentication;
            //判断应用黑白名单
            if (!isNeedAuth(auth2Authentication.getOAuth2Request().getClientId())) {
                return true;
            }

            List<SimpleGrantedAuthority> grantedAuthorityList = (List<SimpleGrantedAuthority>) authentication.getAuthorities();
            if (CollectionUtil.isEmpty(grantedAuthorityList)) {
                log.warn("角色列表为空：{}", authentication.getPrincipal());
                return false;
            }

            //保存租户信息
            String clientId = auth2Authentication.getOAuth2Request().getClientId();
            TenantContextHolder.setTenant(clientId);

            String roleCodes = grantedAuthorityList.stream().map(SimpleGrantedAuthority::getAuthority).collect(Collectors.joining(", "));
            List<SysMenu> menuList = findMenuByRoleCodes(roleCodes);
            //todo 不是超级管理员需要对菜单中的请求方式进行调整 来匹配过滤器
//            for (SysMenu menu : menuList) {
//                if (StringUtils.isNotEmpty(menu.getUrl()) && antPathMatcher.match(menu.getUrl(), requestURI)) {
//                    if (StrUtil.isNotEmpty(menu.getPathMethod())) {
//                        return requestMethod.equalsIgnoreCase(menu.getPathMethod());
//                    } else {
//                        return true;
//                    }
//                }
//            }

        }

        return false;
    }

    /**
     * 判断应用是否满足黑白名单的过滤逻辑
     * @param clientId
     * @return
     */
    private boolean isNeedAuth(String clientId){
        boolean result = true;
        //白名单
        List<String> includeClientIds = securityProperties.getAuth().getUrlPermission().getIncludeClientIds();
        //黑名单
        List<String> exclusiveClientIds = securityProperties.getAuth().getUrlPermission().getExclusiveClientIds();
        if (includeClientIds.size() > 0) {
            result = includeClientIds.contains(clientId);
        } else if(exclusiveClientIds.size() > 0) {
            result = !exclusiveClientIds.contains(clientId);
        }
        return result;
    }


}
